package com.jaws.core.common.util;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.SystemPropertyUtils;

import com.google.common.collect.Lists;

/**
 * 基于spring的类扫描器
 * @author maojian
 * @date 2017-09-28 16:32:05
 * @since v1.0.0
 */
public class ClassScaner implements ResourceLoaderAware {

    //保存过滤规则要排除的注解
    private  List<TypeFilter> includeFilters ;
    private  List<TypeFilter> excludeFilters ;

    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);

    public ClassScaner(){
    	
    }
    
    public ClassScaner(List<TypeFilter> includeFilters, List<TypeFilter> excludeFilters){
    	this.includeFilters = includeFilters;
    	this.excludeFilters = excludeFilters;
    }
    
    public final ResourceLoader getResourceLoader() {
        return this.resourcePatternResolver;
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils
                .getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(
                resourceLoader);
    }

    public void addIncludeFilter(TypeFilter includeFilter) {
        this.includeFilters.add(includeFilter);
    }

    public void addExcludeFilter(TypeFilter excludeFilter) {
        this.excludeFilters.add(0, excludeFilter);
    }

    public void resetFilters(boolean useDefaultFilters) {
        this.includeFilters.clear();
        this.excludeFilters.clear();
    }

	public Set<Class<?>> doScan(String basePackage) {
    	
        Set<Class<?>> classes = new HashSet<Class<?>>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                    + org.springframework.util.ClassUtils
                    .convertClassNameToResourcePath(SystemPropertyUtils
                            .resolvePlaceholders(basePackage))
                    + "/**/*.class";
            Resource[] resources = this.resourcePatternResolver
                    .getResources(packageSearchPath);

            for (int i = 0; i < resources.length; i++) {
                Resource resource = resources[i];
                if (resource.isReadable()) {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    if (matches(metadataReader)) {
                        try {
                            classes.add(Class.forName(metadataReader
                                    .getClassMetadata().getClassName()));
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "I/O failure during classpath scanning", ex);
        }
        return classes;
    }

    protected boolean matches(MetadataReader metadataReader) throws IOException {
    	boolean flag = false;
    	if(null != this.excludeFilters){
	        for (TypeFilter tf : this.excludeFilters) {
	            if (tf.match(metadataReader, this.metadataReaderFactory)) {
	                return false;
	            }
	        }
    	}
    	if(null != this.includeFilters){
	        for (TypeFilter tf : this.includeFilters) {
	            if (!flag && tf.match(metadataReader, this.metadataReaderFactory)) {
	            	flag = true;
	            }
	        }
    	}
        return flag;
    }

    public static void main(String[] args) {
    	TypeFilter filter = new TypeFilter() {
			@Override
			public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
				return metadataReader.getClassMetadata().isAnnotation();
			}
		};
    	List<TypeFilter> includeFilter = Lists.newArrayList(filter);
        Set<Class<?>> sets = new ClassScaner(includeFilter, null).doScan("com.yyfax.framework.validator");
        for(Class<?> c : sets){
        	System.out.println(c.getName());
        }
            
    }
}
